#include <stdlib.h>
#include <stdio.h>
#include "slave.h"
#include "3d7p_aceMesh.h"
#include "task.h"
#include "ldm_malloc.h"
__thread_local volatile unsigned long get_reply,put_reply;

//_thread_local_fix double A_slave[2][BLKX+2][BLKY+2][BLKZ+2];
#if ((BLKX+2)*(BLKY+2)*(BLKZ+2)*2*8/1024)<64
__thread_local double A_lhs[BLKX][BLKY][BLKZ+2];
__thread_local double A_rhs[BLKX+2][BLKY+2][BLKZ+2];
#else 
#if ((TASK_BSIZE_1+2)*(TASK_BSIZE_2+2)*(BLKZ+2)*2*8/1024)<64
__thread_local double A_lhs[TASK_BSIZE_1][TASK_BSIZE_2][BLKZ+2];
__thread_local double A_rhs[TASK_BSIZE_1+2][TASK_BSIZE_2+2][BLKZ+2];
#define TASK_FLOW_TWO 
__thread_local int ix,jy;
#else
__thread_local double A_lhs[TASK_BSIZE_1][TASK_BSIZE_2][TASK_BSIZE_3];
__thread_local double A_rhs[TASK_BSIZE_1+2][TASK_BSIZE_2+2][TASK_BSIZE_3+2];
#define TASK_FLOW_THREE
__thread_local int ix,jy,kz,ixx;
#endif
#endif
//Record the logical information of this time executed task
__thread_local int dest,src,i,j,k,s_iter,s_starti,s_endi,s_startj,s_endj,s_startk,s_endk;
//Record the logical information of the last time executed task.
__thread_local int last_iter,last_starti,last_startj,last_startk;

extern double A[2][SIZEX][SIZEY][SIZEZ];

#define alpha_d 0.0876
#define beta_d  0.0765
__thread_local int core_id=0;
#ifdef _PROFILING
__thread_local volatile unsigned long start,end;
__thread_local volatile unsigned long dma_start1,dma_end1, dma_start2,dma_end2,dma_start3,dma_end3;
__thread_local volatile unsigned long s,e;
__thread_local volatile unsigned long trans_s1,trans_e1,trans_s2,trans_e2,trans_s3,trans_e3;
extern int hit[64];
extern unsigned long slave_comp_count[64];
extern unsigned long slave_trans_count[64];
//extern unsigned long slave_dma_hit_count[64];
extern unsigned long slave_dma_count[64];
unsigned long rtc_()
{
  unsigned long rpcc;
  asm volatile("rcsr  %0,4":"=r"(rpcc));
  return rpcc;
}
#endif
void stencil_core(targs* args)                                      
{  
   //0. assumptions       
   //data region: [starti-1,endi] [startj-1,endj] [startk-1,endk]  
   //compute region: [starti, endi) [startj,endj) [startk,endk)
   long  array_size; 
   int tsize, bsize, stride,len;
   unsigned long ic1,ic2;
   int comp_i,comp_j,comp_k;
   int ii,jj,kk;
   s_iter = args->iter;
   dest = (s_iter) % 2 ;
   src = 1 - dest;
   s_starti = args->starti;
   s_endi = args->endi;
   s_startj = args->startj;
   s_endj = args->endj;
   s_startk = 1;
   s_endk = args->k_N-1;

#ifdef TASK_FLOW_TWO
       for(ix=s_starti;ix<s_endi;ix+=TASK_BSIZE_1){
            for(jy=s_startj;jy<s_endj;jy+=TASK_BSIZE_2){
        bsize = SIZEZ * (TASK_BSIZE_2 + 2) * 8;
        stride = ((SIZEY - (TASK_BSIZE_2 + 2)) * SIZEZ) * 8;  // stride: from blocktail to blockhead
        tsize = bsize * (TASK_BSIZE_1 + 2);
#ifdef _PROFILING
        dma_start2 = rtc_();
#endif
        get_reply = 0;
        athread_get(PE_MODE, &A[src][ix - 1][jy - 1][s_startk - 1], &A_rhs[0][0][0],
                    tsize, (void *)&get_reply, 0, stride, bsize);
        while (get_reply != 1)
            ;
#ifdef _PROFILING
        dma_end2 = rtc_();
#endif

#ifdef _PROFILING
    start = rtc_();
#endif
    // compute
    /*for(ii = 1; ii < BLKX+1; ++ii)
        for(jj = 1; jj < BLKY+1; ++jj)
          for(kk = 1; kk < BLKZ+1; ++kk)
     {
 //    if(ii==1&&jj==1&&kk==1)
   //
 printf("slave:iter%d,%f,%f,%f,%f,%f,%f,%f\n",s_iter,A_rhs[ii][jj][kk],A_rhs[(ii-1)][jj][kk],A_rhs[(ii+1)][jj][kk]
 ,\
               A_rhs[ii][jj-1][kk],A_rhs[ii][(jj+1)][kk],\
               A_rhs[ii][jj][(kk-1)],A_rhs[ii][jj][(kk+1)]);
             A_lhs[ii-1][jj-1][kk] = alpha_d * (A_rhs[ii][jj][kk]) + \
                      beta_d * (A_rhs[(ii-1)][jj][kk] + A_rhs[(ii+1)][jj][kk] +\
                      A_rhs[ii][jj-1][kk] + A_rhs[ii][(jj+1)][kk]+\
                      A_rhs[ii][jj][(kk-1)] + A_rhs[ii][jj][(kk+1)]);
 }*/
    for (ii = 1; ii < TASK_BSIZE_1 + 1; ++ii)
        for (jj = 1; jj < TASK_BSIZE_2 + 1; ++jj)
            for (kk = 1; kk < BLKZ - BLKZ % 4; kk += 4) {
                A_lhs[ii - 1][jj - 1][kk] =
                    alpha_d * (A_rhs[ii][jj][kk]) +
                    beta_d * (A_rhs[(ii - 1)][jj][kk] + A_rhs[(ii + 1)][jj][kk] +
                              A_rhs[ii][jj - 1][kk] + A_rhs[ii][(jj + 1)][kk] +
                              A_rhs[ii][jj][(kk - 1)] + A_rhs[ii][jj][(kk + 1)]);
                A_lhs[ii - 1][jj - 1][kk + 1] =
                    alpha_d * (A_rhs[ii][jj][kk + 1]) +
                    beta_d * (A_rhs[(ii - 1)][jj][kk + 1] + A_rhs[(ii + 1)][jj][kk + 1] +
                              A_rhs[ii][jj - 1][kk + 1] + A_rhs[ii][(jj + 1)][kk + 1] +
                              A_rhs[ii][jj][(kk)] + A_rhs[ii][jj][(kk + 2)]);
                A_lhs[ii - 1][jj - 1][kk + 2] =
                    alpha_d * (A_rhs[ii][jj][kk + 2]) +
                    beta_d * (A_rhs[(ii - 1)][jj][kk + 2] + A_rhs[(ii + 1)][jj][kk + 2] +
                              A_rhs[ii][jj - 1][kk + 2] + A_rhs[ii][(jj + 1)][kk + 2] +
                              A_rhs[ii][jj][(kk + 1)] + A_rhs[ii][jj][(kk + 3)]);
                A_lhs[ii - 1][jj - 1][kk + 3] =
                    alpha_d * (A_rhs[ii][jj][kk + 3]) +
                    beta_d * (A_rhs[(ii - 1)][jj][kk + 3] + A_rhs[(ii + 1)][jj][kk + 3] +
                              A_rhs[ii][jj - 1][kk + 3] + A_rhs[ii][(jj + 1)][kk + 3] +
                              A_rhs[ii][jj][(kk + 2)] + A_rhs[ii][jj][(kk + 4)]);
            }
    for (ii = 1; ii <TASK_BSIZE_1 + 1; ++ii)
        for (jj = 1; jj < TASK_BSIZE_2 + 1; ++jj)
            for (kk = BLKZ - BLKZ % 4; kk < BLKZ + 1; kk += 1) {
                A_lhs[ii - 1][jj - 1][kk] =
                    alpha_d * (A_rhs[ii][jj][kk]) +
                    beta_d * (A_rhs[(ii - 1)][jj][kk] + A_rhs[(ii + 1)][jj][kk] +
                              A_rhs[ii][jj - 1][kk] + A_rhs[ii][(jj + 1)][kk] +
                              A_rhs[ii][jj][(kk - 1)] + A_rhs[ii][jj][(kk + 1)]);
            }
#ifdef _PROFILING
    end = rtc_();
    slave_comp_count[core_id] += ((end - start));
    dma_start3 = rtc_();
#endif
    // 4 write back
    for (i = 0; i < TASK_BSIZE_1; i++)
        for (j = 0; j < TASK_BSIZE_2; j++) {
            A_lhs[i][j][0] = A_rhs[i + 1][j + 1][0];
            A_lhs[i][j][BLKZ + 1] = A_rhs[i + 1][j + 1][BLKZ + 1];
        }
    put_reply = 0;
    bsize = (BLKZ + 2) * (TASK_BSIZE_2)*8;
    stride = ((SIZEY - (TASK_BSIZE_2)) * (BLKZ + 2)) * 8;
    tsize = bsize * (TASK_BSIZE_1);
    athread_put(PE_MODE, &A_lhs[0][0][0], &A[dest][ix][jy][s_startk - 1], tsize,
                (void *)&put_reply, stride, bsize);
    while (put_reply != 1)
        ;
            }
       }
#else
#ifdef TASK_FLOW_THREE
        for(ix=s_starti;ix<s_endi;ix+=TASK_BSIZE_1){
            for(jy=s_startj;jy<s_endj;jy+=TASK_BSIZE_2){
             for(kz=s_startk;kz<s_endk;kz+=TASK_BSIZE_3){
        bsize =(TASK_BSIZE_3 + 2) * 8;
        stride = (SIZEZ - (TASK_BSIZE_3 + 2))  * 8;  // stride: from blocktail to blockhead
        tsize = bsize * (TASK_BSIZE_2 + 2);
#ifdef _PROFILING
        dma_start2 = rtc_();
#endif
       for(ixx=0;ixx<TASK_BSIZE_1+2;ixx++){ 
        get_reply = 0;
        athread_get(PE_MODE, &A[src][ix - 1+ixx][jy - 1][kz - 1], &A_rhs[ixx][0][0],
                    tsize, (void *)&get_reply, 0, stride, bsize);
        while (get_reply != 1)
            ;
      }
#ifdef _PROFILING
        dma_end2 = rtc_();
#endif

#ifdef _PROFILING
    start = rtc_();
#endif
    for (ii = 1; ii <TASK_BSIZE_1 + 1; ++ii)
        for (jj = 1; jj < TASK_BSIZE_2 + 1; ++jj)
            for (kk =1; kk < TASK_BSIZE_3 + 1; ++kk) {
                A_lhs[ii - 1][jj - 1][kk-1] =
                    alpha_d * (A_rhs[ii][jj][kk]) +
                    beta_d * (A_rhs[(ii - 1)][jj][kk] + A_rhs[(ii + 1)][jj][kk] +
                              A_rhs[ii][jj - 1][kk] + A_rhs[ii][(jj + 1)][kk] +
                              A_rhs[ii][jj][(kk - 1)] + A_rhs[ii][jj][(kk + 1)]);
            }
#ifdef _PROFILING
    end = rtc_();
    slave_comp_count[core_id] += ((end - start));
    dma_start3 = rtc_();
#endif
    // 4 write back
   /* for (i = 0; i < TASK_BSIZE_1; i++)
        for (j = 0; j < TASK_BSIZE_2; j++) {
            A_lhs[i][j][0] = A_rhs[i + 1][j + 1][0];
            A_lhs[i][j][TASK_BSIZE_3 + 1] = A_rhs[i + 1][j + 1][TASK_BSIZE_3 + 1];
        }*/
    //put_reply = 0;
    bsize =(TASK_BSIZE_3)*8;
    stride = (SIZEZ - TASK_BSIZE_3) * 8;
    tsize = bsize * (TASK_BSIZE_2);
   for(ixx=0;ixx<TASK_BSIZE_1;ixx++){
    put_reply = 0;
    athread_put(PE_MODE, &A_lhs[ixx][0][0], &A[dest][ix+ixx][jy][kz], tsize,
                (void *)&put_reply, stride, bsize);
    while (put_reply != 1)
        ;
       }
            }
       }
}
#else
//if reuse fetch
   if((s_iter-1) == last_iter && s_starti == last_starti && s_startj == last_startj && s_startk == last_startk)
   {
     core_id = athread_get_id(-1);
//     hit[core_id]++;
#ifdef _PROFILING
     dma_start1=rtc_();
     trans_s1=rtc_();
#endif
    //1.core data from lhs to rhs
     for(i = 0; i < BLKX; i++)
       for(j = 0; j < BLKY; j++)
        for(k = 0; k < BLKZ; k++)
            A_rhs[i+1][j+1][k+1] = A_lhs[i][j][k+1]; 
#ifdef _PROFILING
     trans_e1=rtc_();
#endif
    //2.update up and down shade data (mmem->rhs)
     get_reply = 0;
     athread_get(PE_MODE,&A[src][s_starti-1][s_startj][s_startk-1]
                 , &A_rhs[0][1][0]
                 , (BLKY)*(BLKZ+2)*8, (void*)&get_reply
                 , 0, 0, 0);
     athread_get(PE_MODE,&A[src][s_endi][s_startj][s_startk-1]
                 , &A_rhs[BLKX+1][1][0]
                 , (BLKY)*(BLKZ+2)*8,(void*)&get_reply
                 , 0, 0, 0);
     //2. update left and right shade data (mmem->lhs->rhs)
     //2.1 update left  data (mmem->lhs)
      stride = ((SIZEY-1)*SIZEZ)*8;
      len = BLKX*SIZEZ*8;
      bsize = SIZEZ*8;
      athread_get(PE_MODE,&A[src][s_starti][s_startj-1][s_startk-1]
                   , &A_lhs[0][0][0]
                   , len, (void*)&get_reply
                   , 0, stride, bsize);
      while(get_reply!=3);
#ifdef _PROFILING
     trans_s2=rtc_();
#endif
      //2.2 update left  data(lhs->rhs)
      for(i = 0; i < BLKX; i++)
        for(k = 0; k < BLKZ+2; k++)
          A_rhs[i+1][0][k]=A_lhs[0][i][k];
#ifdef _PROFILING
     trans_e2=rtc_();
#endif
      //2.3 update right  data (mmem->lhs)
      get_reply=0;
      athread_get(PE_MODE,&A[src][s_starti][s_endj][s_startk-1]
                   , &A_lhs[0][0][0]
                   , len,(void*)&get_reply
                   , 0, stride, bsize);  
      while(get_reply!=1);
#ifdef _PROFILING
     trans_s3=rtc_();
#endif
      //2.4 update right data(lhs->rhs)
      for(i = 0; i < BLKX; i++)
         for(k = 0; k < BLKZ+2; k++)
           A_rhs[i+1][BLKY+1][k]=A_lhs[0][i][k];
#ifdef _PROFILING
     trans_e3=rtc_();
#endif
#ifdef _PROFILING
  dma_end1=rtc_();
#endif
   }else{
//no reuse ldm
//1.1  fetch data
   bsize=SIZEZ*(BLKY+2)*8;
   stride=((SIZEY-(BLKY+2))*SIZEZ)*8;  //stride: from blocktail to blockhead 
   tsize= bsize*(BLKX+2);
#ifdef _PROFILING
   dma_start2=rtc_();
#endif
   get_reply=0;   
   athread_get(PE_MODE,&A[src][s_starti-1][s_startj-1][s_startk-1]
               , &A_rhs[0][0][0]
               , tsize, (void*)&get_reply
               , 0, stride, bsize);
   while(get_reply!=1);
#ifdef _PROFILING
  dma_end2=rtc_();
#endif
   last_iter = s_iter;
   last_starti = s_starti;
   last_startj = s_startj;
   last_startk = s_startk;
  }
#ifdef _PROFILING
  start=rtc_();
#endif
 //compute
   /*for(ii = 1; ii < BLKX+1; ++ii)
       for(jj = 1; jj < BLKY+1; ++jj)
         for(kk = 1; kk < BLKZ+1; ++kk)
    {
//    if(ii==1&&jj==1&&kk==1)
  //      printf("slave:iter%d,%f,%f,%f,%f,%f,%f,%f\n",s_iter,A_rhs[ii][jj][kk],A_rhs[(ii-1)][jj][kk],A_rhs[(ii+1)][jj][kk] ,\
              A_rhs[ii][jj-1][kk],A_rhs[ii][(jj+1)][kk],\
              A_rhs[ii][jj][(kk-1)],A_rhs[ii][jj][(kk+1)]);
            A_lhs[ii-1][jj-1][kk] = alpha_d * (A_rhs[ii][jj][kk]) + \
                     beta_d * (A_rhs[(ii-1)][jj][kk] + A_rhs[(ii+1)][jj][kk] +\
                     A_rhs[ii][jj-1][kk] + A_rhs[ii][(jj+1)][kk]+\
                     A_rhs[ii][jj][(kk-1)] + A_rhs[ii][jj][(kk+1)]);
}*/
   for(ii = 1; ii < BLKX+1; ++ii)
       for(jj = 1; jj < BLKY+1; ++jj)
         for(kk = 1; kk < BLKZ-BLKZ%4; kk+=4)
         {
            A_lhs[ii-1][jj-1][kk] = alpha_d * (A_rhs[ii][jj][kk]) + \
                     beta_d * (A_rhs[(ii-1)][jj][kk] + A_rhs[(ii+1)][jj][kk] +\
                     A_rhs[ii][jj-1][kk] + A_rhs[ii][(jj+1)][kk]+\
                     A_rhs[ii][jj][(kk-1)] + A_rhs[ii][jj][(kk+1)]);
            A_lhs[ii-1][jj-1][kk+1] = alpha_d * (A_rhs[ii][jj][kk+1]) + \
                     beta_d * (A_rhs[(ii-1)][jj][kk+1] + A_rhs[(ii+1)][jj][kk+1] +\
                     A_rhs[ii][jj-1][kk+1] + A_rhs[ii][(jj+1)][kk+1]+\
                     A_rhs[ii][jj][(kk)] + A_rhs[ii][jj][(kk+2)]);
            A_lhs[ii-1][jj-1][kk+2] = alpha_d * (A_rhs[ii][jj][kk+2]) + \
                     beta_d * (A_rhs[(ii-1)][jj][kk+2] + A_rhs[(ii+1)][jj][kk+2] +\
                     A_rhs[ii][jj-1][kk+2] + A_rhs[ii][(jj+1)][kk+2]+\
                     A_rhs[ii][jj][(kk+1)] + A_rhs[ii][jj][(kk+3)]);
            A_lhs[ii-1][jj-1][kk+3] = alpha_d * (A_rhs[ii][jj][kk+3]) + \
                     beta_d * (A_rhs[(ii-1)][jj][kk+3] + A_rhs[(ii+1)][jj][kk+3] +\
                     A_rhs[ii][jj-1][kk+3] + A_rhs[ii][(jj+1)][kk+3]+\
                     A_rhs[ii][jj][(kk+2)] + A_rhs[ii][jj][(kk+4)]);
         }
     for(ii = 1; ii < BLKX+1; ++ii)
       for(jj = 1; jj < BLKY+1; ++jj)
         for(kk = BLKZ-BLKZ%4; kk < BLKZ+1; kk+=1)
         {
            A_lhs[ii-1][jj-1][kk] = alpha_d * (A_rhs[ii][jj][kk]) + \
                     beta_d * (A_rhs[(ii-1)][jj][kk] + A_rhs[(ii+1)][jj][kk] +\
                     A_rhs[ii][jj-1][kk] + A_rhs[ii][(jj+1)][kk]+\
                     A_rhs[ii][jj][(kk-1)] + A_rhs[ii][jj][(kk+1)]);
         }
#ifdef _PROFILING
  end=rtc_();
  slave_comp_count[core_id]+=((end-start));
  dma_start3=rtc_();
#endif
//4 write back
   for(i=0;i<BLKX;i++)
     for(j=0;j<BLKY;j++)
     {
        A_lhs[i][j][0]=A_rhs[i+1][j+1][0];
        A_lhs[i][j][BLKZ+1]=A_rhs[i+1][j+1][BLKZ+1];
     }
   put_reply=0;
   bsize=(BLKZ+2)*(BLKY)*8;
   stride=((SIZEY-(BLKY))*(BLKZ+2))*8; 
   tsize= bsize*(BLKX);
   athread_put(PE_MODE,&A_lhs[0][0][0]
                , &A[dest][s_starti][s_startj][s_startk-1]
                ,tsize,(void*)&put_reply,stride,bsize);
   while(put_reply!=1);

#endif
#endif

#ifdef _PROFILING
  dma_end3=rtc_();
  slave_dma_count[core_id]+=((dma_end1-dma_start1)+(dma_end2-dma_start2)+(dma_end3-dma_start3));
//  slave_dma_hit_count[core_id]+=((dma_end1-dma_start1));
  slave_trans_count[core_id]+=((trans_e1-trans_s1)+(trans_e2-trans_s2)+(trans_e3-trans_s3));
#endif
}
